grammar plp3;

@header
{
	import java.lang.String;
}

@members
{
	TablaSimbolos tablaSimbolos = new TablaSimbolos();
	Stack<Integer> pilaEtiquetas = new Stack<Integer>();
	Stack<String> pilaBucles = new Stack<String>();
	Stack<Integer> pilaCatch = new Stack<Integer>();
	Traductor traductor = new Traductor();
	final String initHeader = "/* plp.il */ \n\n.assembly extern mscorlib {}\n.assembly 'plp' {} \n\n\n";
	int nivel = 0;
	int numEtiq = 0;

	public void LanzarError(String error)
	{
		System.err.println(error);
		System.exit (1);
	}
}

@lexer::members
{
	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
}

@rulecatch 
{ 
	catch (RecognitionException re)
	{ 
		reportError(re); 
		System.exit(1); 
	} 
} 


/* SINTAX READER */
s :	
	clase
	{
		System.out.println( initHeader + $clase.trad);
	};

clase
returns [ String trad ]
@init {StringBuilder sb = new StringBuilder(); trad = "";} :  
	CLASS SINGLE
	{
		SimboloClase s = new SimboloClase("clase", new TipoClase(null));
		tablaSimbolos.addSimbolo(s);
	}
	LLAVEI
	{
		tablaSimbolos = tablaSimbolos.push();
		nivel++;
	}
	metodo
	{
		sb.append(tablaSimbolos.toString());
		sb.append($metodo.trad);
		sb.append("}\n");
		$trad = sb.toString();
	}
	LLAVED;

metodo
returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :  
	PUBLIC STATIC VOID MAIN PARI PARD
	{
		SimboloFuncion s = new SimboloFuncion("main", null);
		tablaSimbolos.addSimbolo(s);
	}
	bloque[true]
	{
		salida.append(s.toString(tablaSimbolos.getLocalsValue()));
		salida.append($bloque.trad).append("ret\n}\n");
		$trad = salida.toString();
		tablaSimbolos = tablaSimbolos.pop();
	};

tipoSimple returns [ Tipo tipo, Integer linea, Integer col ]
@init { $linea = -1; $col = -1;}: 
		INT 
		{
			$tipo = new TipoEntero();
			$linea = $INT.getLine();
			$col = $INT.getCharPositionInLine()+1;
		}
	| 
		DOUBLE
		{
			$tipo = new TipoReal();
			$linea = $DOUBLE.getLine();
			$col = $DOUBLE.getCharPositionInLine()+1;
		}
	|
		BOOL
		{
			$tipo = new TipoBooleano();
			$linea = $BOOL.getLine();
			$col = $BOOL.getCharPositionInLine()+1;
		}
	;

decl returns [ String trad ]
@init {trad = "";} :
		constdecl 
		{
			$trad = $constdecl.trad;
		}
	|
		vardecl
		{
			$trad = $vardecl.trad;
		}
	;

constdecl returns [ String trad ]
@init {trad = ""; StringBuilder salida = new StringBuilder();} :
	CONST tipoSimple c1=constid[$tipoSimple.tipo]
	{
		salida.append($c1.trad);
	}
	(
		COMA c2=constid[$tipoSimple.tipo]
		{
			salida.append($c2.trad);
		}
	)*
	PYC
	{
		$trad = salida.toString();
	};

constid[Tipo tipo] returns [ String trad ]
@init {trad = ""; StringBuilder salida = new StringBuilder();} :
	ID ASIG
	(
			ENTERO 
			{
				SimboloEntero s = new SimboloEntero($ID.text, tipo );
				s.setConstante();
				if ( !tablaSimbolos.addSimbolo(s) )
				{
					StringBuilder err = new StringBuilder();
					err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
					err.append("): '").append($ID.text).append("' ya existe en este ambito");
					LanzarError(err.toString());
				}
				if ( !tipo.isEntero() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append(": tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				salida.append("ldc.i4 ").append($ENTERO.text).append("\n");
				salida.append("stloc ").append(s.getLocalPos()).append("\n");
				$trad = salida.toString();
			}
		|
			REAL
			{
				SimboloReal s = new SimboloReal($ID.text, tipo );
				s.setConstante();
				if ( !tablaSimbolos.addSimbolo(s) )
				{
					StringBuilder err = new StringBuilder();
					err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
					err.append("): '").append($ID.text).append("' ya existe en este ambito");
					LanzarError(err.toString());
				}
				if ( !tipo.isReal() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append(": tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				salida.append("ldc.r8 ").append($REAL.text).append("\n");
				salida.append("stloc ").append(s.getLocalPos()).append("\n");
				$trad = salida.toString();
			}
		|
			BOOLEANO
			{
				SimboloBooleano s = new SimboloBooleano($ID.text, tipo);
				s.setConstante();
				if ( !tablaSimbolos.addSimbolo(s) )
				{
					StringBuilder err = new StringBuilder();
					err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
					err.append("): '").append($ID.text).append("' ya existe en este ambito");
					LanzarError(err.toString());
				}
				if ( !tipo.isBoolean() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append(": tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				salida.append("ldc.i4 ");
				if ( $BOOLEANO.text.equals("True") )
				{
					salida.append("1\n");
				}
				else
				{
					salida.append("0\n");
				}
				salida.append("stloc ").append(s.getLocalPos()).append("\n");
				$trad = salida.toString();
			}
	);

vardecl returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	tipoSimple varid[$tipoSimple.tipo]
	(
		COMA varid[$tipoSimple.tipo]
	)*
	PYC;

varid[Tipo tipo]
@init {Tipo tipoFinal;} :
	ID
	{
		Simbolo s;
		if ( tipo.isEntero() )
		{
			s = new SimboloEntero($ID.text, tipo);
		}
		else if ( tipo.isReal() )
		{
			s = new SimboloReal($ID.text, tipo);
		}
		else
		{
			s = new SimboloBooleano($ID.text, tipo);
		}
			
	}
	(
		CORI
		{
			tipoFinal = new TipoArray(tipo, null);
			s.setTipo(tipoFinal);
		}
		(
			{
				TipoArray t = new TipoArray(tipoFinal, null);
				tipoFinal = t;
				s.setTipo(tipoFinal);
			}
			COMA
		)*
		CORD
	)?
	{
		if ( !tablaSimbolos.addSimbolo(s) )
		{
			StringBuilder err = new StringBuilder();
			err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
			err.append("): '").append($ID.text).append("' ya existe en este ambito");
			LanzarError(err.toString());
		}
	};

declins returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	(
		instr[true]
		{
			salida.append($instr.trad);
		}
		|
		decl
		{
			salida.append($decl.trad);
		}
	)*
	{
		$trad = salida.toString();
	};

bloque[boolean pop]
returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	LLAVEI
	{
		if ( pop )
		{
			tablaSimbolos = tablaSimbolos.push();
		}
	}
	declins
	{
		$trad = $declins.trad;
	}
	LLAVED
	{
		if ( pop )
		{
			tablaSimbolos = tablaSimbolos.pop();
		}
	};

instr[boolean pop] returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
		bloque[pop]
		{
			$trad = $bloque.trad;
		}
	| 
		IF PARI e3=expr[null]
		{
			if (  $e3.sr != null && !$e3.sr.isBooleano() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 5 (").append($IF.getLine()).append(",").append($IF.getCharPositionInLine()+1).append("):");
				sb.append(" la expresion debe ser de tipo booleano en la instruccion '");
				sb.append($IF.text).append("'");
				LanzarError( sb.toString() );
			}
			salida.append($e3.trad);
			salida.append("ldc.i4.0\n").append("beq L").append(numEtiq).append(" //SALTO SI FALSO\n"); //Si falso saltar
			pilaEtiquetas.push(numEtiq);
			numEtiq++;
		}
		PARD i1=instr[true]
		{
			int etiq = pilaEtiquetas.pop();
			salida.append($i1.trad);
			salida.append("br L").append(etiq).append("FIN //FIN IF NO ESTRAR ELSE\n");
			salida.append("L").append(etiq).append(": //ELSE\n");
		}
		(
			ELSE i2=instr[true]
			{
				salida.append($i2.trad);
			}
		)?
		{
			salida.append("L").append(etiq).append("FIN: //FIN ELSE\n");
			numEtiq++;
			$trad = salida.toString();
		}
	|
		WHILE PARI e4=expr[null]
		{
			if (  $e4.sr != null && !$e4.sr.isBooleano() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 5 (").append($WHILE.getLine()).append(",").append($WHILE.getCharPositionInLine()+1).append("):");
				sb.append(" la expresion debe ser de tipo booleano en la instruccion '");
				sb.append($WHILE.text).append("'");
				LanzarError( sb.toString() );
			}
			int etiq = numEtiq;
			salida.append("W").append(numEtiq).append(": //BUCLE WHILE \n");
			pilaBucles.push("W" + numEtiq);
			numEtiq++;
			salida.append($e4.trad);
			salida.append("ldc.i4.0\n").append("beq W").append(etiq).append("FIN //COMPROBAR CONDICION WHILE\n"); //Si falso saltar
		}
		PARD i3=instr[true]
		{
			salida.append($i3.trad);
			salida.append("br W").append(etiq).append(" //SIGUIENTE ITERACION DE WHILE\n");
			salida.append("W").append(etiq).append("FIN: //FIN WHILE\n");
			$trad = salida.toString();
			pilaBucles.pop();
		}
	|
		FOR PARI INT ID 
		{
			tablaSimbolos = tablaSimbolos.push();
			Simbolo s = new SimboloEntero($ID.text);
			s.setConstante();
			if ( !tablaSimbolos.addSimbolo(s) )
			{
				StringBuilder err = new StringBuilder();
				err.append("Error 1 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
				err.append("): '").append($ID.text).append("' ya existe en este ambito");
				LanzarError(err.toString());
			}
		}
		ASIG e1=expr[s] TO e2=expr[null] STEP (ADDOP)? ENTERO PARD
		{
			if ( $e2.trad.lastIndexOf("clt") != -1 || $e2.trad.lastIndexOf("cgt") != -1 || $e2.trad.lastIndexOf("ceq") != -1   )
			{
				StringBuilder err = new StringBuilder();
				err.append("Error 17 (").append($TO.getLine()).append(",").append($TO.getCharPositionInLine()+1);
				err.append("): la expresion debe ser de tipo numerico");
				LanzarError(err.toString());
			}
			pilaBucles.push("F" + numEtiq);
			numEtiq++;
		}
		i4=instr[false]
		{
			salida.append($e1.trad);
			salida.append("conv.i\n");
			salida.append("stloc ").append(s.getLocalPos()).append(" //ASIGNACION DEL FOR\n");
			
			String etiq = pilaBucles.pop();
			salida.append(etiq).append(": //INICIO BUCLE FOR \n");
			salida.append($e2.trad);
			salida.append("ldloc ").append(s.getLocalPos()).append("\n");
			if ( $e2.sr.isReal() )
			{
				salida.append("conv.r8 \n");
			}
			if ( $ADDOP != null && $ADDOP.text.equals("-"))
			{
				salida.append("cgt\nldc.i4.1\nxor //MIETRAS ITERADOR > CONDICION \n");
			}
			else
			{
				salida.append("clt\nldc.i4.1\nxor //MIENTRAS ITERADOR < CONDICION \n");
			}
			salida.append("ldc.i4.0\n").append("beq ").append(etiq).append("FIN //COMPROBAR CONDICION FOR\n"); //Si falso saltar
			salida.append($i4.trad);
			salida.append(etiq).append("INC: //INCREMETO DEL ITERADOR\n");
			salida.append("ldloc ").append(s.getLocalPos()).append("\n");
			salida.append("ldc.i4 ").append($ENTERO.text).append("\n");
			if ( $ADDOP != null && $ADDOP.text.equals("-"))
			{
				salida.append("sub\n");
			}
			else
			{
				salida.append("add\n");
			}
			salida.append("stloc ").append(s.getLocalPos()).append("\n");
			salida.append("br ").append(etiq).append("\n");
			salida.append(etiq).append("FIN: //FIN BUCLE FOR\n");
			$trad = salida.toString();
			tablaSimbolos = tablaSimbolos.pop();
		}
	|
		BREAK PYC
		{
			if ( pilaBucles.empty() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 16 (").append($BREAK.getLine()).append(",").append($BREAK.getCharPositionInLine()+1);
				sb.append("): instruccion '");
				sb.append($BREAK.text).append("' no permitida fuera de un bucle");
				LanzarError( sb.toString() );
			}
			else
			{
				salida.append("br ").append(pilaBucles.peek()).append("FIN\n");
				$trad = salida.toString();
			}
		}
	|
		CONTINUE PYC
		{
			if ( pilaBucles.empty() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 16 (").append($CONTINUE.getLine()).append(",").append($CONTINUE.getCharPositionInLine()+1);
				sb.append("): instruccion '");
				sb.append($CONTINUE.text).append("' no permitida fuera de un bucle");
				LanzarError( sb.toString() );
			}
			else
			{
				String salto = pilaBucles.peek();
				if ( salto.charAt(0) == 'F' )
				{
					salida.append("br ").append(pilaBucles.peek()).append("INC\n");
				}
				else
				{
					salida.append("br ").append(pilaBucles.peek()).append("\n");
				}
				$trad = salida.toString();
			}
		}
	|
		ref[""] cambio[$ref.s, $ref.trad]
		{
			$trad = $cambio.trad;
		}
	|
		ID
		{
			Simbolo s = tablaSimbolos.getSimbolo($ID.text);
			if ( s == null )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 2 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): simbolo '");
				sb.append($ID.text).append("' no ha sido declarado");
				LanzarError( sb.toString() );
			}
			if ( s.getTipo() == null || !s.getTipo().isArray() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 11 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): el identificador '");
				sb.append($ID.text);
				sb.append("' no es de tipo array");
				LanzarError( sb.toString() );
			}
		}
		ASIG NEW tipoSimple
		{
			Tipo t = $tipoSimple.tipo;
			if ( !t.getNombre().equals(s.getTipo().getTipoSimple().getNombre()) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 14 (").append($tipoSimple.linea).append(",").append($tipoSimple.col);
				sb.append("): tipo '");
				sb.append(t.getNombre());
				sb.append("' incompatible con la declaracion");
				LanzarError( sb.toString() );
			}
			TipoArray tArray = (TipoArray)s.getTipo();
		}
		CORI dims[tArray] CORD
		{
			if ( tArray.getDimensiones() > $dims.dimensiones)
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 10 (").append($CORD.getLine()).append(",").append($CORD.getCharPositionInLine()+1);
				sb.append("): numero de dimensiones incorrecto");
				LanzarError( sb.toString() );
			}
			tArray.setInicializado();
			salida.append("ldc.i4 ").append(tArray.getTotalSize()).append(" //TAMAÑO DEL VECTOR ").append(s.getNombre()).append(" \n");
			salida.append("newarr [mscorlib]System.").append(tArray.getTipoSimpleString()).append(" //RESERVA DE MEMORIA DEL VECTOR\n");
			salida.append("stloc ").append(s.getLocalPos()).append("\n");
			$trad = salida.toString();
		}
		PYC
	|
		WRITELINE PARI ex1=expr[null]
		{
			salida.append($ex1.trad);
			salida.append("call void [mscorlib]System.Console::WriteLine(");
			salida.append($ex1.cima).append(")\n");
			$trad = salida.toString();
		}
		PARD PYC
	|
		TRY
		{
			pilaEtiquetas.push(numEtiq);
			numEtiq++;
		if ( !pilaCatch.empty() )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 18 (").append($TRY.getLine()).append(",").append($TRY.getCharPositionInLine()+1);
			sb.append("): sentencia '");
			sb.append($TRY.text);
			sb.append("' no permitida en este ambito");
			LanzarError( sb.toString() );
		}
		}
		b2=bloque[true] bloquecatch[pilaEtiquetas.peek()]
		{
			salida.append(".try\n{\n");
			salida.append($b2.trad);
			salida.append("leave L").append(pilaEtiquetas.pop()).append("\n}\n");
			salida.append($bloquecatch.trad);
			$trad = salida.toString();
		}
	|
		THROW ( NEW EXCEPTION PARI ex2=expr[null] PARD )? PYC
		{
			if ( $NEW == null )
			{
				$trad = "rethrow\n";
			}
			else
			{
				salida.append($ex2.trad);
				salida.append("call string [mscorlib]System.Convert::ToString(").append($ex2.cima).append(")\n");
				salida.append("newobj instance void [mscorlib]System.Exception::.ctor(string)\n");
				salida.append("throw\n");
				$trad = salida.toString();
			}
		}
	| 
		EXPLAIN ID PYC
		{
			if ( pilaCatch.empty() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 18 (").append($EXPLAIN.getLine()).append(",").append($EXPLAIN.getCharPositionInLine()+1);
				sb.append("): sentencia '");
				sb.append($EXPLAIN.text);
				sb.append("' no permitida en este ambito");
				LanzarError( sb.toString() );
			}
			if ( !tablaSimbolos.getSimbolo($ID.text).isExcepcion() ) 
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 19 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
				sb.append("): '");
				sb.append($ID.text);
				sb.append("' no es una excepcion");
				LanzarError( sb.toString() );
			}
			salida.append("dup\n");
			salida.append("call instance string [mscorlib]System.Exception::get_Message()\n");
			salida.append("call void [mscorlib]System.Console::WriteLine(string)\n");
			$trad = salida.toString();
		};

bloquecatch[int etiqueta] returns [String trad]
@init {StringBuilder salida = new StringBuilder();}:
	CATCH PARI EXCEPTION ID PARD
	{
		SimboloExcepcion s = new SimboloExcepcion($ID.text);
		tablaSimbolos.addSimbolo(s);
		pilaCatch.push(1);
	}
	bloque[true]
	{
		salida.append("catch [mscorlib]System.Exception\n{\n");
		salida.append($bloque.trad);
		salida.append("leave L").append(etiqueta).append("\n").append("}\n");
		salida.append("L").append(etiqueta).append(":\n");
		$trad = salida.toString();
		pilaCatch.pop();
	};

dims[TipoArray t] returns [int dimensiones]
@init {dimensiones = 0;} :
	e1=ENTERO
	{
		TipoArray t2 = (TipoArray)t;
		t2.setSize($e1.text);
		if ( t2.getSize() < 1 )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 8 (").append($e1.getLine()).append(",").append($e1.getCharPositionInLine()+1);
			sb.append("): tamanyo incorrecto");
			LanzarError( sb.toString() );
		}
		dimensiones++;
	}
	(
		COMA e2=ENTERO
		{
			if ( t2.getTipoBase().isArray() )
			{
				t2 = (TipoArray)t2.getTipoBase();
			}
			dimensiones++;
			if ( t.getDimensiones() < dimensiones)
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 10 (").append($COMA.getLine()).append(",").append($COMA.getCharPositionInLine()+1);
				sb.append("): numero de dimensiones incorrecto");
				LanzarError( sb.toString() );
			}
			t2.setSize($e2.text);
			if ( t2.getSize() < 1 )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 8 (").append($e2.getLine()).append(",").append($e2.getCharPositionInLine()+1);
				sb.append("): tamanyo incorrecto");
				LanzarError( sb.toString() );
			}
		}
	)*;

cambio[Simbolo s, String ref] returns [ String trad ]
@init {StringBuilder salida = new StringBuilder(); $trad = "";} :
		ASIG expr[s] PYC
		{
			if ( s.isConstante() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 15 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
				sb.append("): la variable que se intenta modificar es de solo lectura");
				LanzarError( sb.toString() );
			}
			if ( !s.getTipo().isArray() )
			{
				if( ( s.isBooleano() && !$expr.sr.isBooleano() ) || ( !s.isBooleano() && $expr.sr.isBooleano() ) )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 6 (").append($ASIG.getLine()).append(",").append($ASIG.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de asignacion");
					LanzarError( sb.toString() );
				}
				salida.append($expr.trad);
				if ( s.isEntero() && $expr.sr.isReal() )
				{
					salida.append("conv.i\n");
				}
				else if ( s.isReal() && $expr.sr.isEntero() )
				{
					salida.append("conv.r8\n");
				}
				salida.append("st").append(ref);
			}
			else
			{
				salida.append(ref);
				salida.append($expr.trad);
				if ( s.isEntero() && $expr.sr.isReal() )
				{
					salida.append("conv.i\n");
				}
				else if ( s.isReal() && $expr.sr.isEntero() )
				{
					salida.append("conv.r8\n");
				}
				if ( s.isReal() )
				{
					salida.append("stelem.r8\n");
				}
				else
				{
					salida.append("stelem.i4\n");
				}
			}
			$trad = salida.toString();
		}
	|
		PUNTO READLINE PYC
		{
			if ( s.isConstante() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 15 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
				sb.append("): la variable que se intenta modificar es de solo lectura");
				LanzarError( sb.toString() );
			}
			if ( s.getTipo().isArray() )
			{
				salida.append(ref);
			}
			salida.append("call string [mscorlib]System.Console::ReadLine()\n");
			if ( s.isEntero() )
			{
				if ( $READLINE.text.charAt(0) != 'i' )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 7 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de lectura");
					LanzarError( sb.toString() );
				}
				salida.append("call int32[mscorlib]System.Int32::Parse(string)\n");
			}
			else if ( s.isBooleano() )
			{
				if ( $READLINE.text.charAt(0) != 'b' )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 7 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de lectura");
					LanzarError( sb.toString() );
				}
				salida.append("call bool[mscorlib]System.Boolean::Parse(string)\n");
			}
			else
			{
				if ( $READLINE.text.charAt(0) != 'd' )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 7 (").append($READLINE.getLine()).append(",").append($READLINE.getCharPositionInLine()+1);
					sb.append("): tipos incompatibles en la instruccion de lectura");
					LanzarError( sb.toString() );
				}
				salida.append("call float64[mscorlib]System.Double::Parse(string)\n");
			}
			if ( !s.getTipo().isArray() )
			{
				salida.append("stloc ").append(s.getLocalPos()).append("\n");
			}
			else
			{
				if ( s.isReal() )
				{
					salida.append("stelem.r8\n");
				}
				else
				{
					salida.append("stelem.i4\n");
				}
			}
			$trad = salida.toString();
		};

expr[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = "";} :
	e1=eand[sh]
	{
		$sr = $e1.sr;
		salida.append($e1.trad);
	}
	(
		OR e2=eand[sh]
		{
			if (  $e1.sr != null && $e2.sr != null && ( !$e2.sr.isBooleano() || !$e1.sr.isBooleano() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 4 (").append($OR.getLine()).append(",").append($OR.getCharPositionInLine()+1).append("):");
				sb.append(" tipo incompatible en operador logico '");
				sb.append($OR.text).append("'");
				LanzarError( sb.toString() );
			}
			$sr = new SimboloBooleano("");
			$cima = "bool";
			salida.append($e2.trad);
			salida.append("or\n");
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $e1.cima;
		}
		salida.append("\n");
		$trad = salida.toString();
	};

eand[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = "";} :
	e1=erel[sh]
	{
		salida.append($e1.trad);
		$sr = $e1.sr;
	}
	(
		AND e2=erel[sh]
		{
			$cima = "bool";
			$sr = new SimboloBooleano("");
			salida.append($e2.trad);
			salida.append("and\n");
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $e1.cima;
		}
		$trad = salida.toString();
	};

erel[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = ""; Simbolo sant = null;} :
	e1=esum[sh]
	{
		salida.append($e1.trad);
		$sr = $e1.sr;
		sant = $e1.sr;
	}
	(
		RELOP e2=esum[$e1.sr]
		{
			$cima = "bool";
			$sr = new SimboloBooleano("");
			if (  sant != null && $e2.sr != null && $e2.sr.isReal())
			{
				salida.append("conv.r8\n");
			}
			salida.append($e2.trad);
			if ( sant != null && $e2.sr != null && sant.isReal())
			{
				salida.append("conv.r8\n");
			}
			salida.append(traductor.get($RELOP.text)).append("\n");
			sant = $sr;
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $e1.cima;
		}
		$trad = salida.toString();
	};

esum[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = ""; $sr = null;} :
	t1=term[sh]
	{
		salida.append($t1.trad);
		$sr = $t1.sr;
	}
	(
		ADDOP t2=term[$sr]
		{
			if (  $t1.sr != null && $t2.sr != null && ( $t2.sr.isBooleano() || $t1.sr.isBooleano() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 3 (").append($ADDOP.getLine()).append(",").append($ADDOP.getCharPositionInLine()+1).append("):");
				sb.append(" tipo incompatible en operador aritmetico '");
				sb.append($ADDOP.text).append("'");
				LanzarError( sb.toString() );
			}
			$cima = "int32";
			if (  $t1.sr != null && $t2.sr != null && $t2.sr.isReal())
			{
				salida.append("conv.r8\n");
				$sr = $t2.sr;
			}
			salida.append($t2.trad);
			if ( $t1.sr != null && $t2.sr != null && $t1.sr.isReal())
			{
				salida.append("conv.r8\n");
			}
			salida.append(traductor.get($ADDOP.text)).append("\n");
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $t1.cima;
		}
		$trad = salida.toString();
	};

term[Simbolo sh] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = "";} :
	f1=factor[sh, ""]
	{
		salida.append($f1.trad);
		$cima = $f1.cima;
		$sr = $f1.sr;
	}
	(
		MULOP f2=factor[sh, "mul"]
		{
			if (  $f1.sr != null && $f2.sr != null && ( $f2.sr.isBooleano() || $f1.sr.isBooleano() ) )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 3 (").append($MULOP.getLine()).append(",").append($MULOP.getCharPositionInLine()+1).append("):");
				sb.append(" tipo incompatible en operador aritmetico '");
				sb.append($MULOP.text).append("'");
				LanzarError( sb.toString() );
			}
			$cima = "int32";
			if (  $f1.sr != null && $f2.sr != null && $f2.sr.isReal())
			{
				salida.append("conv.r8\n");
				$sr = $f2.sr;
			}
			salida.append($f2.trad);
			if ( $f1.sr != null && $f2.sr != null && $f1.sr.isReal())
			{
				salida.append("conv.r8\n");
			}
			salida.append(traductor.get($MULOP.text)).append("\n");
		}
	)*
	{
		if ( $cima.equals("") )
		{
			$cima = $f1.cima;
		}
		$trad = salida.toString();
	};

factor[Simbolo sh, String op] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = "";} :
	base[sh, op]
	{
		$trad = $base.trad;
		$cima = $base.cima;
		$sr = $base.sr;
	}
	|
		NOT f1=factor[sh, op]
	{
		if (  $f1.sr != null && !$f1.sr.isBooleano() )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 4 (").append($NOT.getLine()).append(",").append($NOT.getCharPositionInLine()+1).append("):");
			sb.append(" tipo incompatible en operador logico '");
			sb.append($NOT.text).append("'");
			LanzarError( sb.toString() );
		}
		salida.append($f1.trad);
		salida.append("ldc.i4.1\n");
		salida.append("xor\n");
		$trad = salida.toString();
		$cima = $f1.cima;
		$sr = f1.sr;
	}
	|
		PARI ADDOP f2=factor[sh, op] PARD
		{
			salida.append($f2.trad);
			if ( $ADDOP.text.equals("-"))
			{
				salida.append("neg\n");
			}
			$trad = salida.toString();
			$cima = $f2.cima;
			$sr = f2.sr;
		}
	;

base[Simbolo sh, String op] returns [ String cima, String trad, Simbolo sr ]
@init {StringBuilder salida = new StringBuilder(); $cima = ""; $trad = ""; $sr = null;} :
	(
			ENTERO
			{
				if ( sh != null && sh.isReal() )
				{
					salida.append("ldc.r8 ").append($ENTERO.text).append("\n");
				}
				else
				{
					salida.append("ldc.i4 ").append($ENTERO.text).append("\n");
				}
				$sr = new SimboloEntero("simbolo");
				$trad = salida.toString();
				$cima = "int32";
			}
		|
			REAL
			{
				salida.append("ldc.r8 ").append($REAL.text).append("\n");
				$trad = salida.toString();
				$sr = new SimboloReal("");
				$cima = "float64";
			}
		|
			BOOLEANO
			{
				salida.append("ldc.i4 ");
				if ( $BOOLEANO.text.equals("True"))
					salida.append("1\n");
				else
					salida.append("0\n");
				$trad = salida.toString();
				$sr = new SimboloBooleano("");
				$cima = "bool";
			}
	)
	|
		PARI expr[sh] PARD
		{
			$trad = $expr.trad;
			$cima = $expr.cima;
			$sr = $expr.sr;
		}
	| 
		ref[op]
		{
			if ( !$ref.s.getTipo().isArray() )
			{
				salida.append("ld").append($ref.trad);
			}
			else
			{
				salida.append($ref.trad);
				if ( $ref.s.isReal() )
				{
					salida.append("ldelem.r8\n");
				}
				else
				{
					salida.append("ldelem.i4\n");
				}
			}
			$trad = salida.toString();
			$cima = $ref.cima;
			$sr = $ref.s;
		};

ref[String op] returns [ String trad, String cima, Simbolo s, int fila, int columna ]
@init {StringBuilder salida = new StringBuilder(); $trad = ""; $cima = "";} :
	ID
	{
		$fila = $ID.getLine();
		$columna = $ID.getCharPositionInLine()+1;
		$s = tablaSimbolos.getSimbolo($ID.text);
		if ( $s == null )
		{
			StringBuilder sb = new StringBuilder();
			sb.append("Error 2 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1).append("): simbolo '");
			sb.append($ID.text).append("' no ha sido declarado");
			LanzarError( sb.toString() );
		}
		if ( !$s.getTipo().isArray() )
		{
			salida.append("loc ").append( $s.getLocalPos() ).append("\n");
			$cima = $s.toWrite();
		}
		else
		{
			$cima = $s.getTipo().getTipoBase().toWrite();
		}
	}
	(
		CORI
		{
			if ( !$s.getTipo().isArray() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 11 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
				sb.append("): el identificador '").append($ID.text).append("' no es de tipo array");
				LanzarError( sb.toString() );
			}
		}
		indices[$s, op] CORD
		{
			TipoArray tArr = (TipoArray)$s.getTipo();
			if ( $indices.indice < ( tArr.getDimensiones() + 1 ))
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 9 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
				sb.append("): numero insuficiente de indices en el array '");
				sb.append($ID.text);
				sb.append("'");
				LanzarError( sb.toString() );
			}
			salida.append("ldloc ").append($s.getLocalPos()).append("\n");
			salida.append( $indices.trad );	
			if ( !$s.getTipo().isArray() )
			{
				StringBuilder sb = new StringBuilder();
				sb.append("Error 11 (").append($ID.getLine()).append(",").append($ID.getCharPositionInLine()+1);
				sb.append("): el identificador '");
				sb.append($ID.text);
				sb.append("' no es de tipo array");
				LanzarError( sb.toString() );
			}
			if ( tArr.getTipoSimple().isReal() )
				$cima = "float64";
			else if (tArr.getTipoSimple().isEntero() )
				$cima = "int32";
			else
				$cima = "bool";
				
		}
	)?
	{
		$trad = salida.toString();
	};

indices[Simbolo s, String op] returns[String trad, int indice]
@init{StringBuilder salida = new StringBuilder(); $indice = 1; $trad = "";}:
	e1=expr[new SimboloEntero("")]
	{
		TipoArray t = (TipoArray)s.getTipo();
		if ( t != null && t.isInicializado() )
		{
			salida.append("//Inicio acceso a vector\nldc.i4.0 \n");
			salida.append($e1.trad);
			salida.append("conv.i\n");
			if ( t != null )
				salida.append("ldc.i4 ").append(t.getParcialSize($indice)).append("\n");
			else
				salida.append("ldc.i4 0\n");
			salida.append("mul\n");
			salida.append("add\n");
		}
		$indice++;
	}
	(
		COMA e2=expr[s]
		{
			if ( t != null && t.isInicializado() && !(op.equals("mul") &&  s.isBooleano() ) )
			{
				if ( $e2.sr.isBooleano() )
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 13 (").append($COMA.getLine()).append(",").append($COMA.getCharPositionInLine()+1);
					sb.append("): indice de tipo incompatible");
					LanzarError( sb.toString() );
				}
				if ( t != null && $indice < t.getDimensiones() )
				{
					salida.append($e2.trad);
					salida.append("conv.i\n");
					salida.append("ldc.i4 ").append(t.getParcialSize($indice)).append("\n");
					salida.append("mul\n");
					salida.append("add\n");
				}
				else if ( t != null && $indice == t.getDimensiones() )
				{
					salida.append($e2.trad);
					salida.append("conv.i\n");
					salida.append("add\n");
				}
				else
				{
					StringBuilder sb = new StringBuilder();
					sb.append("Error 12 (").append($COMA.getLine()).append(",").append($COMA.getCharPositionInLine()+1);
					sb.append("): demasiados indices");
					LanzarError( sb.toString() );
				}
			}
			$indice++;
		}
	)*
	{
		salida.append("//FIN ACCESO VECTOR\n");
		$trad = salida.toString();
	};

/* Analizador léxico: */ 
CLASS		:	'class';
SINGLE		:	'Single';
VOID		:	'void';
MAIN		:	'Main';
INT			:	'int';
DOUBLE		:	'double';
BOOL		:	'bool';
PUBLIC		:	'public';
STATIC		:	'static';
IF			:	'if';
ELSE		:	'else';
FOR			:	'for';
TO			:	'to';
STEP		:	'step';
WHILE		:	'while';
BREAK		:	'break';
CONTINUE	:	'continue';
NEW			:	'new';
WRITELINE	:	'System.Console.WriteLine';
READLINE	:	('int'|'double'|'bool')'.Parse(System.Console.ReadLine())';
LLAVEI		:	'{';
LLAVED		:	'}';
PARI		:	'(';
PARD		:	')';
CORI		:	'[';
CORD		:	']';
COMA		:	',';
PYC			:	';';
ASIG		:	'=';
OR			:	'|';
AND			:	'&';
RELOP		:	'=='|'!='|'<' | '>' | '<=' | '>=';
ADDOP		:	'+' | '-';
MULOP		:	'*' | '/';
NOT			:	'!';
PUNTO		:	'.';
ENTERO		:	('0'..'9')+;
REAL		:	('0'..'9')+'.'('0'..'9')+;
BOOLEANO	:	'True' | 'False';
CONST		:	'const';
EXPLAIN		:	'explain';
TRY			:	'try';
CATCH		:	'catch';
THROW		:	'throw';
EXCEPTION	:	'Exception';
ID			:	('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
NEWLINE		:	('\r'? '\n'|' '|'\t')+  {skip();}; 
COMENTARIO	:	'/*' .* '*/' {skip();};
COMENLINEA	:	'//' .* '\n'  {skip();};
